import json
import time
import requests
from typing import Dict, List
import threading
import random
import numpy as np

from config.base_config import TOPO_CONFIG_FILE


class DecisionCenter:
    def __init__(self, nodes: Dict[str, str],nodes_type: Dict[str, str]):
        """
        初始化决策中心
        nodes: Dict[节点ID, API地址]
        """
        self.nodes = nodes
        self.nodes_type = nodes_type
        self.running = True
        self.decision_thread = threading.Thread(target=self._make_decisions)
        self.decision_thread.start()
    
    def _get_node_status(self, node_id: str) -> Dict:
        """获取节点状态"""
        try:
            response = requests.get(f"{self.nodes[node_id]}/status")
            return response.json()
        except Exception as e:
            print(f"获取节点 {node_id} 状态失败: {e}")
            return None
    
    def _get_node_queues(self, node_id: str) -> Dict:
        """获取节点队列信息"""
        try:
            response = requests.get(f"{self.nodes[node_id]}/queues")
            return response.json()
        except Exception as e:
            print(f"获取节点 {node_id} 队列信息失败: {e}")
            return None
    
    def _decide_task_placement(self, task: Dict, node_statuses: Dict) -> str:
        """决定任务应该在哪个节点执行"""

        node_ids=[]
        list(node_statuses.keys())
        for id,type in nodes_type.items():
            if task['task_type']==type and id in list(node_statuses.keys()):
                node_ids.append(id)


        return random.choice(node_ids)

    def _get_links(self) -> float:
        return random.random()

    def _get_target_node(self, task: Dict, action: float) -> str:

        # 根据任务类型获取候选节点
        candidate_nodes = []
        for node_id, node_type in self.nodes_type.items():
            if task['task_type'] == node_type:
                candidate_nodes.append(node_id)
        # 如果候选节点为空，则返回None
        if not candidate_nodes:
            return None
        # 根据动作选择目标节点
        N=len(candidate_nodes)
        node_index=((N-1)*action+N+1)/2
        return candidate_nodes[int(node_index)]

    def _get_path(self, node_id: str,node_id1: str, action: int) -> List[str]:
        return []

    def _get_action(self, state: np.ndarray) -> np.ndarray:
        # 输出0-1之间的随机数
        return np.random.rand(len(self.nodes)* 40)


    def _make_decisions(self):
        """持续进行决策"""
        while self.running:
            try:
                print("\n开始新一轮决策...")

                # 所有任务存储起来
                node_tasks = {}
                # 将所有队列信息构造成矩阵
                queues_matrix = []
                for node_id in self.nodes:
                    queues = self._get_node_queues(node_id)

                    # if not queues or not queues['arrival_queue']:
                    #     continue

                    node_tasks[node_id] = queues['arrival_queue']

                    # 任务队列包含 计算量，数据量，最大延迟，需要提取出来
                    queues_matrix.append([[task['compute_load'], task['data_size'], task['max_delay']] for task in
                                          queues['arrival_queue']])
                    queues_matrix.append([[task['compute_load'], task['data_size'], task['max_delay']] for task in
                                          queues['local_queue']])
                    queues_matrix.append([[task['compute_load'], task['data_size'], task['max_delay']] for task in
                                          queues['forward_queue']])
                # 每个队列如果没填满队列，则用0补充任务
                for queue in queues_matrix:
                    if len(queue) < 20:
                        queue.extend([[0, 0, 0] for _ in range(20 - len(queue))])
                    # 如果队列超过10个，则只保留前10个
                    if len(queue) > 20:
                        queue = queue[:20]
                # 将矩阵转换为numpy数组
                queues_matrix = np.array(queues_matrix)
                print(queues_matrix.shape)

                # 获取网络链路使用率
                links_matrix = []
                for l in range(6):
                    links = self._get_links()
                    links_matrix.append(links)
                links_matrix = np.array(links_matrix)
                print(links_matrix)

                # 将两个矩阵拉成一维
                queues_matrix = queues_matrix.flatten()
                links_matrix = links_matrix.flatten()
                # 将两个一维数组拼接成一个一维数组
                state = np.concatenate((queues_matrix, links_matrix), axis=0)
                print(state)

                # 根据强化学习模型进行决策
                action = self._get_action(state)
                print(action)
                # 动作是到达队列节点*最大长度*2（选节点、选路）
                # actionre进行reshape，从一维变为三维
                action = action.reshape(len(self.nodes), 20, 2)

                # 获取所有节点的队列信息并进行决策
                i = 0
                for app_name, tasks_queue in node_tasks.items():
                    j = 0
                    decisions = []
                    for task in tasks_queue:
                        # 获取决策到的计算的目标节点
                        target_node = self._get_target_node(task, action[i][j][0])
                        # 获取决策到的路径
                        path = self._get_path(app_name, target_node, action[i][j][1])
                        decisions.append({
                            "task_id": task['id'],
                            "target_node": target_node,
                            "path": path
                        })
                        j += 1
                    i += 1
                    if decisions:
                        print(decisions)
                        try:
                            import requests
                            response = requests.post(
                                f"{self.nodes[app_name]}/decide_tasks",
                                json={"decisions": decisions}
                            )
                            print(f"决策提交结果: {response.json()}")
                        except Exception as e:
                            print(f"决策提交失败: {e}")

                time.sleep(5)  # 决策间隔
            except Exception as e:
                print(f"决策过程出错: {e}")
                time.sleep(5)
    
    def stop(self):
        """停止决策中心"""
        self.running = False
        self.decision_thread.join() 


topo_config = json.load(open(TOPO_CONFIG_FILE))
nodes={}
nodes_type={}
# for app in topo_config["applications"]:
#     nodes[app["name"]] = f"http://192.168.10.235:{app['port_binding']}"
#     nodes_type[app["name"]] = app["type"]

nodes = {
    "co1": "http://192.168.10.235:8000",
    "co2": "http://192.168.10.235:8001"
}
nodes_type = {
    "co1": "COMPUTE",
    "co2": "COMPUTE"
}
decision_center = DecisionCenter(nodes,nodes_type)